﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace randlib.core
{
    class RandomJava
    {

        /** use serialVersionUID from JDK 1.1 for interoperability */
        const long serialVersionUID = 3905348978240129619L;

        /**
         * The internal state associated with this pseudorandom number generator.
         * (The specs for the methods in this class describe the ongoing
         * computation of this value.)
         */
        long seed;

        const long multiplier = 0x5DEECE66DL;
        const long addend = 0xBL;
        const long mask = (1L << 48) - 1;

        const double DOUBLE_UNIT = 1.0 / ((long)1 << 53);// 0x1.0p-53; // 1.0 / (1L << 53)

        // IllegalArgumentException messages
        const String BadBound = "bound must be positive";
        const String BadRange = "bound must be greater than origin";
        const String BadSize = "size must be non-negative";

        /**
         * Creates a new random number generator. This constructor sets
         * the seed of the random number generator to a value very likely
         * to be distinct from any other invocation of this constructor.
         */
        public RandomJava() : this(GetSeedUniquifier() ^ DateTime.Now.Ticks)
        {



        }

        private static long GetSeedUniquifier()
        {
            // L'Ecuyer, "Tables of Linear Congruential Generators of
            // Different Sizes and Good Lattice Structure", 1999
            for (; ; )
            {
                long current = seedUniquifier;
                long next = current * 181783497276652981L;
                seedUniquifier = next;
                return next;
            }
        }

        private static long seedUniquifier = 8682522807148012L;

        public RandomJava(long seed)
        {
            this.setSeed(seed);
        }


        private static long initialScramble(long seed)
        {
            return (seed ^ multiplier) & mask;
        }


        public void setSeed(long seed)
        {
            this.seed = seed;
        }

        protected int next(int bits)
        {
            long oldseed, nextseed;
            //AtomicLong seed = this.seed;
            //do
            //{
            oldseed = seed;
            nextseed = (oldseed * multiplier + addend) & mask;

            this.seed = nextseed;
            // } while (!seed.compareAndSet(oldseed, nextseed));
            return (int)(nextseed >> (48 - bits));
        }

        public void nextBytes(byte[] bytes)
        {
            for (int i = 0, len = bytes.Length; i < len;)
                for (int rnd = nextInt(),
                         n = Math.Min(len - i, 32 / 8);
                     n-- > 0; rnd >>= 8)
                    bytes[i++] = (byte)rnd;
        }

        public int nextInt()
        {
            return next(32);
        }


        public int nextInt(int bound)
        {
            if (bound <= 0)
                throw new Exception(BadBound);

            int r = next(31);
            int m = bound - 1;
            if ((bound & m) == 0)  // i.e., bound is a power of 2
                r = (int)((bound * (long)r) >> 31);
            else
            {
                for (int u = r;
                     u - (r = u % bound) + m < 0;
                     u = next(31))
                    ;
            }
            return r;
        }


        public long nextLong()
        {
            // it's okay that the bottom word remains signed.
            return ((long)(next(32)) << 32) + next(32);
        }




    }
}
